package com.anysoft.util;

import java.security.Security;
import java.util.*;
import java.util.Map.Entry;

import com.anysoft.formula.Expression;
import com.anysoft.formula.FunctionHelper;
import com.anysoft.formula.Parser;
import com.google.re2j.Matcher;
import com.google.re2j.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.anysoft.formula.DataProvider;

/**
 * 变量集
 * @author duanyy
 * 
 * @version 1.2.3 [20140725 duanyy] <br>
 * - 增加loadFromString <br>
 * 
 * @version 1.6.1.1 [20141118 duanyy] <br>
 * - 实现DataProvider接口 <br>
 * 
 * @version 1.6.3.30 [20150714 duanyy] <br>
 * - 修正缺省值为null的空指针问题 <br>
 * 
 * @version 1.6.4.43 [20160411 duanyy] <br>
 * - DataProvider增加获取原始值接口 <br>
 * 
 * @version 1.6.7.7 [20170126 duanyy] <br>
 * - 增加loadFrom系列方法，用于从Json对象，Element节点，Element属性列表中装入变量列表 <br>
 * 
 * @version 1.6.8.10 [20170418 duanyy] <br>
 * - 在装入xml配置文件时，可从env中获取变量 <br>
 * 
 * @version 1.6.12.36 [20190611 duanyy] <br>
 * - 设置Property环境变量时可计算
 *
 * @version 1.6.13.29 [20201219 duanyy] <br>
 * - 可设置环境变量到Security
 *
 * @version 1.6.14.1 [20210310 duanyy] <br>
 * - 更改为接口
 *
 * @version 1.6.14.4 [20210323 duanyy] <br>
 * - 变量支持缺省值; <br>
 * - 修复内嵌公式解析器的线程安全问题; <br>
 */

public interface Properties extends DataProvider{
	/**
	 * 获取变量值
	 * @param _name 变量名
	 * @param _defaultValue 缺省值
	 * @param _bVariable 是否计算变量
	 * @param _bNoParent 是否搜索父亲节点
	 * @return 变量值
	 */
	public String GetValue(String _name,String _defaultValue,boolean _bVariable,boolean _bNoParent);

	/**
	 * 获取变量值
	 * @param _name 变量名
	 * @param _defaultValue 缺省值
	 * @param _bVariable 是否计算变量
	 * @return 变量值
	 */
	public String GetValue(String _name,String _defaultValue,boolean _bVariable);

	/**
	 * 获取变量值
	 * @param _name 变量名
	 * @param _defaultValue 缺省值
	 * @return 变量值
	 */
	public String GetValue(String _name,String _defaultValue);

	public void list(List<Pair<String,String>> result);

	public void SetValue(String _name,String _value);

	public String transform(String pattern);

	public static abstract class Abstract implements Properties{
		/**
		 * logger of log4j
		 */
		protected static Logger logger = LoggerFactory.getLogger(Properties.class);
		/**
		 * 变量域
		 */
		protected String domain = "Default";
		/**
		 * 父节点
		 */
		protected Properties parent = null;
		/**
		 * 变量开始字符
		 */
		public static final String VariableStart = "${";
		/**
		 * 变量结束字符
		 */
		public static final String VariableEnd = "}";

		public static final String VariablePrefix = "$";

		public static final String VariableToken = ":-";

		public static final String VariableDft = "";

		/**
		 * 公式的开始字符
		 */
		public static final String formulaStart = "#";

		/**
		 * 公式匹配模版
		 */
		protected static final Pattern formulaPattern = Pattern.compile("#(.*)#");

		/**
		 * 公式解析器
		 */
		protected static final ThreadLocal<Parser> formulaParser = new ThreadLocal<Parser>(){
		    @Override
            protected Parser initialValue() {
                return new Parser();
            }
        };

		/**
		 * 构造函数
		 * @param _domain 变量域
		 * @param _parent 父节点
		 */
		public Abstract(String _domain, Properties _parent){
			domain = _domain;
			parent = _parent;
		}

		/**
		 * 构造函数
		 * @param _domain 变量域
		 */
		public Abstract(String _domain){
			this(_domain,null);
		}

		/**
		 * 构造函数
		 */
		public Abstract(){
			this("Default",null);
		}

		/**
		 * 获取变量域
		 * @return domain
		 */
		public String getDomain(){
			return domain;
		}

		/**
		 * 设置变量域
		 * @param _domain
		 */
		public void setDomain(String _domain){
			domain = _domain;
		}

		/**
		 * 设置变量集的父节点
		 * @param _parent 父节点
		 */
		public Properties PutParent(Properties _parent){
			Properties temp = parent;
			parent = _parent;
			return temp;
		}

		/**
		 * 利用变量集变量来计算给定字符串
		 * @param pattern 字符串
		 * @return 计算后的字符串
		 */
		public String transform(String pattern){
			return FillValue("",pattern);
		}

		/**
		 * 从字符串装入变量
		 *
		 * <p>从一个字符串装入变量，字符串的语法如下：</p>
		 * <code>var1=val1;var2=val2</code>
		 * <p>从上面的字符串中可以解析出2个变量:var1和var2，其取值分别是val1和val2</p>
		 */
		public void loadFromString(String _para){
			loadFromString(_para,";","=");
		}

		/**
		 * 从字符串装入变量
		 * @param _para 字符串
		 * @param delimeter1 间隔符
		 * @param delimeter2 间隔符
		 *
		 * @since 1.2.3
		 */
		public void loadFromString(String _para,String delimeter1,String delimeter2){
			StringTokenizer __t = new StringTokenizer(_para,delimeter1);
			while (__t.hasMoreTokens()) {
				String __pair = __t.nextToken();
				int __index = __pair.indexOf(delimeter2);
				if (__index < 0){
					continue;
				}
				String __name = __pair.substring(0,__index);
				String __value = __pair.substring(__index + 1,__pair.length());
				SetValue(__name,__value);
			}
		}

		/**
		 * 从JSON对象的属性列表中装入
		 * @param json Json对象
		 *
		 * @since  1.6.7.7
		 */
		public void loadFrom(Map<String,Object> json){
			Iterator<Entry<String,Object>> iter = json.entrySet().iterator();

			while (iter.hasNext()){
				Entry<String,Object> entry = iter.next();
				String key = entry.getKey();
				Object value = entry.getValue();

				if (value instanceof String || value instanceof Number){
					SetValue(key,value.toString());
				}
			}
		}

		/**
		 * 从Element节点的属性列表中装入
		 * @param elem Element节点
		 *
		 * @since  1.6.7.7
		 */
		public void loadFromElementAttrs(Element elem){
			if (elem != null){
				NamedNodeMap attrs = elem.getAttributes();
				for (int i = 0 ;i < attrs.getLength() ; i ++){
					Node n = attrs.item(i);
					if (Node.ATTRIBUTE_NODE != n.getNodeType()){
						continue;
					}
					Attr attr = (Attr)n;

					String key = attr.getName();
					String value = attr.getValue();

					if (StringUtils.isNotEmpty(key) && StringUtils.isNotEmpty(value)){
						SetValue(key,value);
					}
				}
			}
		}

		/**
		 * 从Element中装入
		 * @param root Element节点
		 *
		 * @since  1.6.7.7
		 */
		public void loadFrom(Element root){
			NodeList nodeList = root.getChildNodes();
			for (int i = 0 ; i < nodeList.getLength() ; i ++){
				Node node = nodeList.item(i);
				if (node.getNodeType() != Node.ELEMENT_NODE){
					continue;
				}
				if ("parameter".equals(node.getNodeName())){
					Element e = (Element)node;
					String id = XmlTools.getString(e,"id","");
					String value = XmlTools.getString(e,"value","");
					if (StringUtils.isEmpty(id)){
						continue;
					}

					if (StringUtils.isNotEmpty(value)){
						boolean fromEnv = XmlTools.getBoolean(e, "fromEnv", false);
						if (fromEnv){
							value = System.getenv(value);
							if (StringUtils.isEmpty(value)){
								value = XmlTools.getString(e, "dft", "");
							}
						}else{
							boolean fromProperties = XmlTools.getBoolean(e, "fromProperties", false);
							if (fromProperties){
								value = System.getProperty(value);
								if (StringUtils.isEmpty(value)){
									value = XmlTools.getString(e, "dft", "");
								}
							}
						}
					}
					//支持final标示,如果final为true,则不覆盖原有的取值
					boolean isFinal = XmlTools.getBoolean(e, "final", false);
					if (isFinal){
						String oldValue = GetValue(id, "", false,false);
						if (StringUtils.isEmpty(oldValue)){
							SetValue(id,value);
							boolean system = XmlTools.getBoolean(e, "system", false);
							if (system){
								System.setProperty(id, transform(value));
								logger.info(String.format("Set system property:%s=%s", id,value));
							}
							boolean security = XmlTools.getBoolean(e,"security",false);
							if (security){
								Security.setProperty(id,transform(value));
								logger.info(String.format("Set security property:%s=%s",id,value));
							}
						}
					}else{
						SetValue(id,value);
						boolean system = XmlTools.getBoolean(e, "system", false);
						if (system){
							System.setProperty(id,transform(value));
							logger.info(String.format("Set system property:%s=%s", id,value));
						}
						boolean security = XmlTools.getBoolean(e,"security",false);
						if (security){
							Security.setProperty(id,transform(value));
							logger.info(String.format("Set security property:%s=%s",id,value));
						}
					}
				}
			}
		}

		/**
		 * 设置变量
		 * @param _name 变量名
		 * @param _value 变量值
		 */
		public void SetValue(String _name,String _value){
			_SetValue(_name,_value);
		}

		/**
		 * 获取变量值
		 * @param _name 变量名
		 * @param _defaultValue 缺省值
		 * @param _bVariable 是否计算变量
		 * @param _bNoParent 是否取父节点的变量
		 * @return 变量值
		 */
		public String GetValue(String _name,String _defaultValue,boolean _bVariable,boolean _bNoParent){
			String __value = _GetValue(_name);
			if (StringUtils.isEmpty(__value)){
				//在本变量集没有找到
				if (parent != null && !_bNoParent){
					//定义了父节点
					__value = parent.GetValue(_name,_defaultValue,false,false);
				}else{
					__value = _defaultValue;
				}
			}
			//在本变量集找到
			if (_bVariable && StringUtils.isNotEmpty(__value)){
				//计算变量
				__value = FillValue(_name,__value);
			}
			return __value;
		}

		/**
		 * 进行变量填充式计算
		 * @param _name 最初的变量名
		 * @param _value 计算的原始值
		 * @return　计算后的取值
		 */
		public String FillValue(String _name,String _value){
			int __startIndex = _value.indexOf(VariableStart);
			if (__startIndex < 0){
				//没有找到变量
				return _value;
			}

			//在变量之前的字符串
			StringBuffer __tmpValue =  new StringBuffer(_value.substring(0,__startIndex));
			int __endIndex = _value.indexOf(VariableEnd,__startIndex + VariableStart.length());
			if (__endIndex < 0){
				//没有发现变量结束符
				return __tmpValue.toString();
			}
			//变量名
			String __varName = _value.substring(
					__startIndex + VariableStart.length(),
					__endIndex - VariableStart.length() + VariableStart.length()
			);

			//在变量之后的字符串
			String __afterValue = _value.substring(__endIndex + VariableEnd.length());

			//取变量的值
			String __varValue;
			if (!__varName.equals(_name)){
				String __varDftValue = VariableDft;
				int dftToken = __varName.indexOf(VariableToken);
				if (dftToken > 1){
					__varDftValue = __varName.substring(dftToken + VariableToken.length());
					if (__varDftValue.startsWith(VariablePrefix)){
                        __varDftValue = GetValue(__varDftValue.substring(VariablePrefix.length()),VariableDft,false,false);
                    }
					__varName = __varName.substring(0,dftToken);
				}
				__varValue = GetValue(__varName,__varDftValue,false,false);
				if (__varValue.length() >= 0){
					__tmpValue.append(__varValue);
				}
				__tmpValue.append(__afterValue);
				return FillValue(_name,__tmpValue.toString());
			}
			return __tmpValue.toString();
		}
		/**
		 * 获取变量值
		 * @param _name 变量名
		 * @param _defaultValue 缺省值
		 * @param _bVariable 是否计算变量
		 * @return 变量值
		 */
		public String GetValue(String _name,String _defaultValue,boolean _bVariable){
			return GetValue(_name,_defaultValue,_bVariable,false);
		}

		/**
		 * 获取变量值
		 * @param _name 变量名
		 * @param _defaultValue 缺省值
		 * @return 变量值
		 */
		public String GetValue(String _name,String _defaultValue){
			return GetValue(_name,_defaultValue,true,false);
		}

		/**
		 * 设置变量
		 * @param _name 变量名
		 * @param _value 变量值
		 */
		abstract protected void _SetValue(String _name,String _value);

		/**
		 * 获取变量
		 * @param name 变量名
		 * @return 变量值
		 */
		protected String _GetValue(String name){
			if (name.startsWith(formulaStart)) {
				Matcher matcher = formulaPattern.matcher(name);
				if (matcher.find()) {
					try {
						String statement = matcher.group(1);
						Expression expr = formulaParser.get().parse(statement);
						if (expr != null) {
							return expr.getValue(this).toString();
						}
					} catch (Exception ex) {
						logger.warn("Failed to compute expr:" + name);
						logger.warn(ex.getMessage());
					}
				}
			}
			return null;
		}

		/**
		 * 清楚变量
		 */
		abstract public void Clear();

		public void list(List<Pair<String,String>> result){

		}

		public String getValue(String varName, Object context, String defaultValue) {
			return GetValue(varName, defaultValue);
		}

		public Object getContext(String varName) {
			return cookies;
		}

		@Override
		public String getRawValue(String varName, Object context, String dftValue) {
			return GetValue(varName,dftValue,false,false);
		}

		protected static Object cookies = new Object();
	}
}